`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

//Uncomment this line to enable printing of all fetched instructions
//`define UTICA_TRACE_INSTR_FETCH

/**
	@file UticaCPU.v
	@author Andrew D. Zonenberg
	@brief 32-bit MIPS-like CPU
	
	@param clk						Global clock
	@param hold						If asserted, CPU freezes completely
	@param irq						Triggers interrupt if asserted
	@param vector					The interrupt vector being triggered by irq
	@param ibus_addr				Instruction bus address (always word aligned)
	@param ibus_rd					Read enable for instruction bus
	@param dbus_addr				Data bus address (always word aligned)
	@param dbus_rd					Read enable for data bus
	@param dbus_wr					Write enable for data bus
	@param dbus_dout				Write data for data bus
	@param dbus_wmask				Byte write mask for data bus
	@param dbus_din				Read data for data bus
	@param dbus_dinok				Asserted by L1 cache if read data is valid
 */
module UticaCPU(
	clk,
	hold,
	irq, vector,
	ibus_addr, ibus_rd,                    ibus_din, ibus_dinok,
	dbus_addr, dbus_rd, dbus_wr, dbus_dout, dbus_wmask, dbus_din, dbus_dinok
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Global stuff
	input wire clk;
	
	input wire hold;								//Completely halt all stages of the pipeline
	
	input wire irq;								//Interrupt enable
	input wire[3:0] vector;						//The interrupt vector being requested
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// I-side memory bus
	output wire[31:0] ibus_addr;				//Address being read
	output wire ibus_rd;							//Read enable
	input wire[31:0] ibus_din;					//Data from L1 cache to us
	input wire ibus_dinok;						//Asserted on L1 cache hit
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// D-side memory bus
	output wire[31:0] dbus_addr;				//Address being read
	output wire dbus_rd;							//Read enable
	output wire dbus_wr;							//Write enable
	output wire[31:0] dbus_dout;				//Data being written to L1 cache
	output wire[3:0] dbus_wmask;				//Write mask
	input wire[31:0] dbus_din;					//Data from L1 cache to us
	input wire dbus_dinok;						//Asserted on L1 cache hit
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Register file
	
	wire[3:0] w_window;
	wire[4:0] writeback_rdid;
	wire[31:0] writeback_rdval;
	wire writeback_regwrite;
	
	wire writeback_lw;
	wire writeback_lb;
	wire writeback_lbu;
	wire writeback_lh;
	wire writeback_lhu;
	wire writeback_mult;
	wire writeback_multu;
	
	wire writeback_mfhi;
	wire writeback_mflo;
	wire[31:0] mdu_hi_forwarded;
	wire[31:0] mdu_lo_forwarded;
	
	//indicates if a divider is active
	//Cannot use "busy" output of divider because we need to be able to cancel a divide once it's started.
	wire div_active;
	
	//Asserted for any memory read
	wire writeback_memread;
	assign writeback_memread = writeback_lw | writeback_lb | writeback_lbu | writeback_lh | writeback_lhu;
	
	wire[3:0] ra_window;
	wire[4:0] decode_rsid;
	wire[31:0] execute_rs_raw;
	
	wire[3:0] rb_window;
	wire[4:0] decode_rtid;
	wire[31:0] execute_rt_raw;
	
	assign w_window = 0;								//Windows not implemented for now, hard-code to zero
	assign ra_window = 0;
	assign rb_window = 0;
	
	//Low-order bits of a byte read that got stripped off for sending out the bus
	wire[1:0] writeback_lowaddr;
	
	wire writeback_cachemiss;
	wire execute_cachemiss;
	
	wire postwb2_mult;
	wire postwb2_multu;
	
	wire[31:0] writeback_rdval_forwarded;
	UticaCPUWritebackForwarding writeback_forwarding (
		.writeback_lw(writeback_lw), 
		.writeback_lb(writeback_lb), 
		.writeback_lbu(writeback_lbu), 
		.writeback_lh(writeback_lh), 
		.writeback_lhu(writeback_lhu), 
		.writeback_lowaddr(writeback_lowaddr), 
		.writeback_memread(writeback_memread),
		.dbus_din(dbus_din), 
		.dbus_dinok(dbus_dinok),
		.writeback_rdval(writeback_rdval), 
		.writeback_rdval_forwarded(writeback_rdval_forwarded),
		.writeback_cachemiss(writeback_cachemiss),
		.writeback_mfhi(writeback_mfhi), 
		.writeback_mflo(writeback_mflo), 
		.postwb2_mult(postwb2_mult), 
		.postwb2_multu(postwb2_multu), 
		.mdu_hi_forwarded(mdu_hi_forwarded), 
		.mdu_lo_forwarded(mdu_lo_forwarded)
		);	
	UticaCPURegisterFile regfile (
		.clk(clk), 
		.w_enable(writeback_regwrite), 
		.w_window(w_window), 
		.w_regid(writeback_rdid), 
		.w_data(writeback_rdval_forwarded), 
		.ra_window(ra_window), 
		.ra_regid(decode_rsid), 
		.ra_data(execute_rs_raw), 
		.rb_window(rb_window), 
		.rb_regid(decode_rtid), 
		.rb_data(execute_rt_raw)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Coprocessors
	wire cp0_w_enable;
	wire[4:0] cp0_w_regid;
	wire[31:0] cp0_w_data;
	wire[4:0] cp0_r_regid;
	wire[31:0] cp0_r_data;
	UticaCPUCoprocessor0 cp0 (
		.clk(clk), 
		.w_enable(cp0_w_enable), 
		.w_regid(cp0_w_regid), 
		.w_data(cp0_w_data), 
		.r_regid(cp0_r_regid), 
		.r_data(cp0_r_data)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Jump/branch/stall logic
	// A couple of signals are declared all the way up here so that we can bubble fetch/decode if jumping
	wire execute_j;
	wire execute_jal;
	wire execute_jalr;
	wire execute_jr;
	wire execute_beq;
	wire execute_bne;
	
	wire execute_bgtz;
	wire execute_blez;
	wire execute_bgez;
	wire execute_bgezal;
	wire execute_bltz;
	wire execute_bltzal;
	
	wire execute_mflo;
	wire execute_mfhi;
	
	wire[31:0] execute_jump_final;
	
	wire[31:0] execute_rs_forwarded;
	wire[31:0] execute_rt_forwarded;
	
	wire stall_for_mem_rs;
	wire stall_for_mem_rt;
	
	wire stall;
	wire execute_jumping;
	
	wire execute_bubble;
	
	wire execute_memread;
	
	wire execute_uses_rs;
	wire execute_uses_rt;
	
	UticaCPUExecuteBranchLogic branch_logic (
		.mem_mult(mem_mult), 
		.mem_multu(mem_multu), 
		.div_active(div_active), 
		.stall_for_mem_rs(stall_for_mem_rs), 
		.stall_for_mem_rt(stall_for_mem_rt), 
		.execute_rs_forwarded(execute_rs_forwarded), 
		.execute_rt_forwarded(execute_rt_forwarded), 
		.execute_mflo(execute_mflo), 
		.execute_mfhi(execute_mfhi), 
		.execute_beq(execute_beq), 
		.execute_bne(execute_bne), 
		.execute_bgtz(execute_bgtz), 
		.execute_blez(execute_blez), 
		.execute_bgez(execute_bgez), 
		.execute_bgezal(execute_bgezal), 
		.execute_bltz(execute_bltz), 
		.execute_bltzal(execute_bltzal), 
		.execute_j(execute_j), 
		.execute_jal(execute_jal), 
		.execute_jr(execute_jr), 
		.execute_jalr(execute_jalr), 
		.execute_bubble(execute_bubble), 
		.stall(stall), 
		.execute_jumping(execute_jumping),
		.execute_uses_rs(execute_uses_rs),
		.execute_uses_rt(execute_uses_rt),
		.execute_cachemiss(execute_cachemiss)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// FETCH stage
	
	wire decode_bubble;
	wire[31:0] decode_pc;
	wire[31:0] execute_pc;
	wire decode_mispredict;
	
	UticaCPUFetchStage fetch_stage(
		.clk(clk), 
		.hold(hold), 
		.stall(stall), 
		.execute_pc(execute_pc),
		.execute_jumping(execute_jumping),
		.execute_jump_final(execute_jump_final),
		.writeback_cachemiss(writeback_cachemiss),
		.decode_bubble(decode_bubble),
		.decode_pc(decode_pc),
		.ibus_addr(ibus_addr), 
		.ibus_rd(ibus_rd), 
		.ibus_dinok(ibus_dinok),
		.mispredict(decode_mispredict)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// DECODE stage
	// We want to do as much decoding as we possibly can now
	// because that means less cracking to do in the execute stage.

	wire execute_div;
	wire execute_divu;

	wire[4:0] execute_rsid;
	wire[4:0] execute_rtid;
	
	wire[31:0] execute_imm_sx;
	wire[31:0] execute_imm_zx;
	wire[4:0] execute_rdid;
	wire[15:0] execute_imm;
	wire[4:0] execute_shamt;
	wire[5:0] execute_func;
	wire execute_rtype;
	wire[31:0] execute_jump_raw;
	
	wire[5:0] execute_opcode;

	UticaCPUDecodeStage decode_stage (
		.clk(clk), 
		.hold(hold), 
		.ibus_din(ibus_din), 
		.ibus_dinok(ibus_dinok), 
		.decode_pc(decode_pc), 
		.decode_bubble(decode_bubble), 
		.stall(stall),
		.writeback_cachemiss(writeback_cachemiss),
		.decode_rsid(decode_rsid),
		.decode_rtid(decode_rtid),
		.execute_div(execute_div), 
		.execute_divu(execute_divu), 
		.execute_beq(execute_beq), 
		.execute_bne(execute_bne), 
		.execute_bgtz(execute_bgtz), 
		.execute_blez(execute_blez), 
		.execute_bgez(execute_bgez), 
		.execute_bgezal(execute_bgezal), 
		.execute_bltz(execute_bltz), 
		.execute_bltzal(execute_bltzal), 
		.execute_j(execute_j), 
		.execute_jr(execute_jr), 
		.execute_jal(execute_jal), 
		.execute_jalr(execute_jalr), 
		.execute_mfhi(execute_mfhi), 
		.execute_mflo(execute_mflo), 
		.execute_imm_sx(execute_imm_sx),
		.execute_imm_zx(execute_imm_zx),
		.execute_rdid(execute_rdid), 
		.execute_regwrite(execute_regwrite), 
		.execute_imm(execute_imm),
		.execute_jump_raw(execute_jump_raw), 
		.execute_shamt(execute_shamt), 
		.execute_rtype(execute_rtype), 
		.execute_func(execute_func),
		.execute_rsid(execute_rsid),
		.execute_rtid(execute_rtid),
		.execute_opcode(execute_opcode),
		.execute_bubble(execute_bubble),
		.execute_uses_rs(execute_uses_rs),
		.execute_uses_rt(execute_uses_rt),
		.decode_cp0_regid(cp0_r_regid),
		.decode_mispredict(decode_mispredict)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// EXECUTE stage
	wire[31:0] mdu_hi;
	wire[31:0] mdu_lo;
	wire[31:0] div_quot;
	wire[31:0] div_rem;
	wire[4:0] mem_rdid;
	wire[31:0] mem_rdval;
	wire mem_overflow_exception;
	
	wire mem_regwrite;
	wire[1:0] mem_lowaddr;
	
	wire[63:0] multout_s;
	wire[63:0] multout_u;
	
	wire mem_mfhi;
	wire mem_mflo;
	
	UticaCPUExecuteStage execute_stage (
		.clk(clk), 
		.hold(hold), 
		.dbus_addr(dbus_addr), 
		.dbus_wmask(dbus_wmask), 
		.dbus_wr(dbus_wr), 
		.dbus_rd(dbus_rd), 
		.dbus_dout(dbus_dout), 
		.decode_pc(decode_pc), 
		.execute_pc(execute_pc), 
		.execute_jump_raw(execute_jump_raw), 
		.execute_jump_final(execute_jump_final), 
		.execute_rs_raw(execute_rs_raw), 
		.execute_rt_raw(execute_rt_raw), 
		.execute_imm(execute_imm), 
		.execute_imm_sx(execute_imm_sx), 
		.execute_imm_zx(execute_imm_zx), 
		.execute_jumping(execute_jumping), 
		.execute_memread(execute_memread), 
		.execute_rsid(execute_rsid), 
		.execute_rtid(execute_rtid), 
		.execute_rdid(execute_rdid), 
		.execute_regwrite(execute_regwrite),
		.execute_shamt(execute_shamt), 
		.execute_rtype(execute_rtype),
		.execute_func(execute_func),
		.execute_rs_forwarded(execute_rs_forwarded), 
		.execute_rt_forwarded(execute_rt_forwarded), 
		.execute_div(execute_div), 
		.execute_divu(execute_divu), 
		.execute_jalr(execute_jalr), 
		.execute_jr(execute_jr), 
		.execute_opcode(execute_opcode),
		.mem_rdval(mem_rdval), 
		.mem_rdid(mem_rdid), 
		.mem_regwrite(mem_regwrite), 
		.mem_lw(mem_lw), 
		.mem_lb(mem_lb), 
		.mem_lbu(mem_lbu), 
		.mem_lh(mem_lh), 
		.mem_lhu(mem_lhu), 
		.mem_mult(mem_mult), 
		.mem_multu(mem_multu), 
		.postwb2_mult(postwb2_mult), 
		.postwb2_multu(postwb2_multu), 
		.div_active(div_active), 
		.writeback_rdid(writeback_rdid), 
		.writeback_rdval(writeback_rdval), 
		.writeback_regwrite(writeback_regwrite), 
		.writeback_memread(writeback_memread), 
		.writeback_cachemiss(writeback_cachemiss),
		.div_quot(div_quot), 
		.div_rem(div_rem), 
		.div_done(div_done), 
		.mdu_hi(mdu_hi), 
		.mdu_lo(mdu_lo), 
		.stall_for_mem_rs(stall_for_mem_rs), 
		.stall_for_mem_rt(stall_for_mem_rt), 
		.mem_overflow_exception(mem_overflow_exception), 
		.mem_lowaddr(mem_lowaddr), 
		.multout_s(multout_s), 
		.multout_u(multout_u),
		.execute_bubble(execute_bubble),
		.cp0_r_data(cp0_r_data),
		.mem_cp0_w_regid(cp0_w_regid), 
		.mem_cp0_w_enable(cp0_w_enable), 
		.mem_cp0_w_data(cp0_w_data),
		.mdu_lo_forwarded(mdu_lo_forwarded),
		.mdu_hi_forwarded(mdu_hi_forwarded),
		.mem_mfhi(mem_mfhi),
		.mem_mflo(mem_mflo),
		.writeback_mfhi(writeback_mfhi),
		.writeback_mflo(writeback_mflo),
		.writeback_lw(writeback_lw), 
		.writeback_lb(writeback_lb), 
		.writeback_lbu(writeback_lbu), 
		.writeback_lh(writeback_lh), 
		.writeback_lhu(writeback_lhu),
		.writeback_rdval_forwarded(writeback_rdval_forwarded),
		.execute_cachemiss(execute_cachemiss)
		);

	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// MEM stage
	
	UticaCPUMemStage mem_stage (
		.clk(clk), 
		.hold(hold), 
		.mem_rdid(mem_rdid), 
		.mem_rdval(mem_rdval), 
		.mem_regwrite(mem_regwrite), 
		.mem_overflow_exception(mem_overflow_exception), 
		.mem_lw(mem_lw), 
		.mem_lb(mem_lb), 
		.mem_lbu(mem_lbu), 
		.mem_lh(mem_lh), 
		.mem_lhu(mem_lhu), 
		.mem_lowaddr(mem_lowaddr), 
		.mem_mult(mem_mult), 
		.mem_multu(mem_multu), 
		.mem_mfhi(mem_mfhi), 
		.mem_mflo(mem_mflo), 
		.writeback_lw(writeback_lw), 
		.writeback_lb(writeback_lb), 
		.writeback_lbu(writeback_lbu), 
		.writeback_lh(writeback_lh), 
		.writeback_lhu(writeback_lhu), 
		.writeback_lowaddr(writeback_lowaddr), 
		.writeback_mult(writeback_mult), 
		.writeback_multu(writeback_multu), 
		.writeback_rdid(writeback_rdid), 
		.writeback_rdval(writeback_rdval), 
		.writeback_regwrite(writeback_regwrite),
		.writeback_mfhi(writeback_mfhi), 
		.writeback_mflo(writeback_mflo)
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// WRITEBACK stage
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Post-writeback processing for the multiplier and divider	
	
	UticaCPUPostWritebackStage post_writeback_stage (
		.clk(clk), 
		.multout_s(multout_s), 
		.multout_u(multout_u), 
		.div_done(div_done), 
		.div_active(div_active), 
		.div_quot(div_quot), 
		.div_rem(div_rem), 
		.mdu_lo(mdu_lo), 
		.mdu_hi(mdu_hi), 
		.writeback_mult(writeback_mult), 
		.writeback_multu(writeback_multu), 
		.postwb2_mult(postwb2_mult), 
		.postwb2_multu(postwb2_multu)
		);

endmodule
